package io.yx

import cn.hutool.core.collection.CollectionUtil
import cn.hutool.core.io.FileUtil
import cn.hutool.core.io.resource.ResourceUtil
import cn.hutool.core.util.ArrayUtil
import cn.hutool.core.util.StrUtil
import cn.hutool.http.HttpUtil
import cn.hutool.json.JSONObject
import cn.hutool.json.JSONUtil
import cn.hutool.setting.Setting
import cn.hutool.setting.SettingUtil
import java.io.File
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.collections.ArrayList

// 从类路径读取wx.setting文件
var setting: Setting? = null

val newAppId: String? by lazy {
    setting?.getStr("newAppId")
}
val newAppSecret: String? by lazy {
    setting?.getStr("newAppSecret")
}

// 新账号的accessToken
var newAccessToken: String? = null

// 旧账号的appid
val oldAppId: String? by lazy {
    setting?.getStr("oldAppId")
}

//旧账号的accessToken
val oldAccessToken: String? by lazy {
    setting?.getStr("oldAccessToken")
}

// 获取到的用户的openid集合
var openIdList = ArrayList<String>()


fun getAccessToken(): String {
    //获取accessToken接口
    val getAccessTokenUrl =
        "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${newAppId}&secret=${newAppSecret}"
    val responseString = HttpUtil.get(getAccessTokenUrl)
    val respJson = JSONUtil.parseObj(responseString)
    val accessToken = respJson.getStr("access_token") ?: throw RuntimeException("获取accessToken失败:${responseString}")
    return accessToken
}


fun getUserList(oldAccessToken: String?, nextOpenId: String? = null): ArrayList<String> {
    //关注接口
    val getUserListUrl =
        "https://api.weixin.qq.com/cgi-bin/user/get?next_openid=NEXT_OPENID&access_token=${oldAccessToken}"

    val requestUrl = StrUtil.replace(getUserListUrl, "NEXT_OPENID", nextOpenId)

    val responseString = HttpUtil.get(requestUrl)

    val jsonObject = JSONUtil.parseObj(responseString)

    val errMsg = jsonObject.getStr("errmsg")
    if (errMsg != null) {
        throw RuntimeException("发生错误: $errMsg")
    }
    val total = jsonObject.getInt("total")
    val nextOpenid = jsonObject.getStr("next_openid")

    println("已经获取数量${openIdList.size},总数量${total}")

    // 如果集合中的数量与总数不等,则继续递归调用
    if (StrUtil.isNotBlank(nextOpenid)) {
        val openIdArray = jsonObject.getJSONObject("data").getJSONArray("openid")
        val iterator = openIdArray.iterator()
        while (iterator.hasNext()) {
            openIdList.add(iterator.next().toString())
        }
        getUserList(oldAccessToken, nextOpenid)
    }

    return openIdList
}


fun transfer(openIds: List<String>): List<String> {
    //转换接口
    val transferUrl = "https://api.weixin.qq.com/cgi-bin/changeopenid?access_token=${newAccessToken}"
    if (openIds.size <= 100) {
        val bodyJson = JSONObject()
        bodyJson.set("from_appid", oldAppId)
        bodyJson.set("openid_list", openIds)
        val response = HttpUtil.post(transferUrl, bodyJson.toString())
        val jsonResp = JSONUtil.parseObj(response)
        val errMsg = jsonResp.getStr("errmsg")
        if (!Objects.equals("ok", errMsg)) {
            throw RuntimeException("发生错误:${errMsg}")
        }
        val mappingArray = jsonResp.getJSONArray("result_list")

        val sqlScriptLines = ArrayList<String>()
        // 定义存储每一行sql的集合
        mappingArray.jsonIter().forEach { i ->
            val err = i.getStr("err_msg")
            if (!Objects.equals(err, "ok")) {
                println("发生错误${i}")
            } else {
                val originOpenId = i.getStr("ori_openid")
                val newOpenId = i.getStr("new_openid")
                sqlScriptLines.add("update table_name set open_id='${newOpenId}' where open_id='${originOpenId}';")
            }
        }
        return sqlScriptLines
    } else {
        val openIdGroups = CollectionUtil.split(openIds, 100)
        val sqlScriptLines = ArrayList<String>()
        // 方法一: 直接循环
//        for (openIdGroup in openIdGroups) {
//            sqlScriptLines.addAll(transfer(openIdGroup))
//        }

        // 方法二: 使用CompletableFuture
        val futures = ArrayList<CompletableFuture<List<String>>>()
        for (openIdGroup in openIdGroups) {
            val future = CompletableFuture.supplyAsync {
                transfer(openIdGroup)
            }
            futures.add(future)
        }
        CompletableFuture.allOf(*futures.toTypedArray()).thenApply {
            println("共${futures.size}个任务,所有任务均已完成")
            futures.forEach {
                val sqlLine = it.join()
                sqlScriptLines.addAll(sqlLine)
            }
        }.join()

        return sqlScriptLines
    }
}

fun main() {


    val userDir = System.getProperty("user.dir")
    val settingFilePath = "${userDir}${File.separator}wx.setting"
    if (!FileUtil.exist(settingFilePath)) {
        val configBytes = ResourceUtil.getResource("wx.setting")?.readBytes()
        FileUtil.writeBytes(configBytes, settingFilePath)
    }

    setting = Setting(settingFilePath)

    val userList = getUserList(oldAccessToken)

    newAccessToken = getAccessToken()

    println("newAccessToken:$newAccessToken")

    val sqlLineList = transfer(userList)

    FileUtil.writeUtf8Lines(sqlLineList, setting?.getStr("sqlFilePath"))

    println("运行完成!共${sqlLineList.size}条")
}
